#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/time.h>

#define SIZE_ARRAY 125
#define MICRO_PER_SECOND 1000000

int serversock[3];

// Inicia as conexoes
int start_connection(char *ip,int porta[3]){

    // Conexão com o cliente 1
    struct sockaddr_in echoserver1;
    if ((serversock[0] = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
        printf("Erro ao iniciar socket 1");
        return 1;
    }

    memset(&echoserver1,0,sizeof(echoserver1));
    echoserver1.sin_family=AF_INET;
    echoserver1.sin_addr.s_addr=htonl(INADDR_ANY);
    echoserver1.sin_port=htons(porta[0]);

    if(bind(serversock[0],(struct sockaddr *)&echoserver1,sizeof(echoserver1))<0){
        printf("Erro na funcao Bind 1");
        return 1;
    }

    listen(serversock[0],1);
    while((serversock[0]=accept(serversock[0],0,0))==-1){
    }

    // Conexão com o cliente 2
    struct sockaddr_in echoserver2;
    if ((serversock[1] = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
        printf("Erro ao iniciar socket 2");
        return 1;
    }

    memset(&echoserver2,0,sizeof(echoserver2));
    echoserver2.sin_family=AF_INET;
    echoserver2.sin_addr.s_addr=htonl(INADDR_ANY);
    echoserver2.sin_port=htons(porta[1]);

    if(bind(serversock[1],(struct sockaddr *)&echoserver2,sizeof(echoserver2))<0){
        printf("Erro na funcao Bind 2");
        return 1;
    }

    listen(serversock[1],1);
    while((serversock[1]=accept(serversock[1],0,0))==-1){
    }

    // Conexão com o Servidor de 3 Nível
    struct sockaddr_in echoserver3;
    if((serversock[2]=socket(PF_INET,SOCK_STREAM,IPPROTO_TCP))<0){
        printf("Erro ao criar socket\n");
        return 1;
    }

    memset(&echoserver3,0,sizeof(echoserver3));
    echoserver3.sin_family=AF_INET;
    echoserver3.sin_addr.s_addr=inet_addr(ip);
    echoserver3.sin_port=htons(porta[2]);
    if(connect(serversock[2],(struct sockaddr *)&echoserver3,sizeof(echoserver3))<0){
        printf("Erro ao conectar ao servidor!\n");
        return 1;
    }

    return 0;
}

int *merge(int *vet1,int *vet2,int size){

    int i=0,j=0,k; //Indices para percorrer o vetor

    // Aloca tamanho do vetor resultante
    int *res=(int*)malloc(2*size*sizeof(int)+1);

    for(k=0;k<size*2;k++){
        if(j>=size || (i<size && vet1[i]<=vet2[j])){   //Insere o proximo valor do vetor 1
            res[k]=vet1[i];
            i++;
        }
        else{                   //Insere o proximo valor do vetor 2
            res[k]=vet2[j];
            j++;
        }
    }
    return res;
}

// Imprime vetor na tela
void print_array(int *vet,int size){
    int i;
    for(i=0;i<size;i++){
        printf("%d ",vet[i]);
    }
    printf("\n");
}

int main(int argc,char *argv[]){

    int bytes;
    char *ip=argv[1];
    int porta[3];
    porta[0]=atoi(argv[2]);  // porta de entrada Cliente 1
    porta[1]=atoi(argv[3]);  // porta de entrada Cliente 2
    porta[2]=atoi(argv[4]);  // porta de saida Servidor de terceiro nivel

    int vet1[SIZE_ARRAY*2];       // Vetor do cliente 1
    int vet2[SIZE_ARRAY*2];       // Vetor do cliente 2
    int *vetMerge; // Junção dos dois vetores
    int vet_final[SIZE_ARRAY*8];

    struct timeval start_time;
    struct timeval stop_time;
    float time_com,time_ord;

    start_connection(ip,porta);

    printf("Servidor nivel 2 conectado!\n");

    bytes=recv(serversock[0],vet1,sizeof(int)*SIZE_ARRAY*2,0);
    bytes=recv(serversock[1],vet2,sizeof(int)*SIZE_ARRAY*2,0);
    if(bytes==-1){
        printf("Erro ao receber vetor");
        return 1;
    }

    gettimeofday( &start_time, NULL );  // Marca o tempo antes da ordenação do vetor

    vetMerge=merge(vet1,vet2,SIZE_ARRAY*2);

    gettimeofday( &stop_time, NULL );

    time_ord = (float)(stop_time.tv_sec - start_time.tv_sec);
    time_ord += (stop_time.tv_usec - start_time.tv_usec)/(float)MICRO_PER_SECOND;

    //print_array(vetMerge,SIZE_ARRAY*2);

    gettimeofday( &start_time, NULL );  // Marca o tempo antes do envio do vetor

    // Envia vetor resultante ao servidor de 3 nivel
    send(serversock[2],vetMerge,sizeof(int)*SIZE_ARRAY*4,0);

    // Recebe vetor final
    recv(serversock[2],vet_final,sizeof(int)*SIZE_ARRAY*8,MSG_WAITALL);

    gettimeofday( &stop_time, NULL );

    time_com = (float)(stop_time.tv_sec - start_time.tv_sec);
    time_com += (stop_time.tv_usec - start_time.tv_usec)/(float)MICRO_PER_SECOND;

    printf("\nTempo de ordenacao do servidor de 2 nivel: %f",time_ord);

    printf("\nTempo de comunicacao do servidor de 2 nivel: %f",time_com);

    // Envia vetor final aos clientes
    send(serversock[0],vet_final,sizeof(int)*SIZE_ARRAY*8,0);
    send(serversock[1],vet_final,sizeof(int)*SIZE_ARRAY*8,0);

    close(serversock[0]);
    close(serversock[1]);
    close(serversock[2]);

    return 0;
}

